home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 June / MacFormat 25.iso / Shareware City / Developers / OutOfPhase1.1 Source / OutOfPhase Folder / Level 0 Macintosh 01Jan95 / Network.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-27  |  40.8 KB  |  1,306 lines  |  [TEXT/KAHL]

  1. /* Network.c */
  2. /*****************************************************************************/
  3. /*                                                                           */
  4. /*    System Dependency Library for Building Portable Software               */
  5. /*    Macintosh Version                                                      */
  6. /*    Written by Thomas R. Lawrence, 1993 - 1994.                            */
  7. /*                                                                           */
  8. /*    This file is Public Domain; it may be used for any purpose whatsoever  */
  9. /*    without restriction.                                                   */
  10. /*                                                                           */
  11. /*    This package is distributed in the hope that it will be useful,        */
  12. /*    but WITHOUT ANY WARRANTY; without even the implied warranty of         */
  13. /*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                   */
  14. /*                                                                           */
  15. /*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
  16. /*                                                                           */
  17. /*****************************************************************************/
  18.  
  19. #include "MiscInfo.h"
  20. #include "Audit.h"
  21. #include "Debug.h"
  22. #include "Definitions.h"
  23.  
  24. #ifdef THINK_C
  25.     #pragma options(pack_enums)
  26. #endif
  27. #include <memory.h>
  28. #include <GestaltEqu.h>
  29. #include <PPCToolbox.h>
  30. #include <Script.h>
  31. #include <Errors.h>
  32. #ifdef THINK_C
  33.     #pragma options(!pack_enums)
  34. #endif
  35.  
  36. #include "Network.h"
  37. #include "Memory.h"
  38. #include "EventLoop.h"
  39. #include "Array.h"
  40.  
  41.  
  42. /* this is the AppleTalk PPCToolBox version of the Network module */
  43.  
  44.  
  45. /* since this system can only handle one outstanding requested connection at */
  46. /* a time, we might have to try several times to establish a connection.  these */
  47. /* specify how many times to try and how long to wait between each one. */
  48. #define MAXNUMTRIES (10)
  49. #define TRYDELAY (1.0) /* in seconds */
  50.  
  51. #define DEFAULTTYPE "PPCToolBox"
  52. #define DEFAULTTYPELENGTH (10)
  53.  
  54. /* size of a single buffer record */
  55. #define BUFFERSIZE (256)
  56.  
  57. typedef enum
  58.     {
  59.         eAwaitingConnection EXECUTE(= -8982),
  60.         eConnectionPending
  61.     } PortStates;
  62.  
  63. struct PortIDType
  64.     {
  65.         PPCPortRefNum                    ThePortRefnum;
  66.         PortStates                        PortState;
  67.         PPCInformPBRec                ThePPCInformPBRec;
  68.         PPCPortRec                        ThePPCPortRec;
  69.         PPCPortRec                        RemotePortName; /* temporary buffer for it */
  70.         LocationNameRec                RemoteLocationName; /* temporary buffer */
  71.     };
  72.  
  73. typedef struct BufferRec
  74.     {
  75.         struct BufferRec*            Next;
  76.         long                                    NumBytes;
  77.         char                                    Buffer[BUFFERSIZE];
  78.     } BufferRec;
  79.  
  80. typedef enum
  81.     {
  82.         eWriteIdle EXECUTE(= -4152),
  83.         eWriteInProgress,
  84.         eWriteFinished
  85.     } SessionWriteStates;
  86.  
  87. typedef enum
  88.     {
  89.         eReadIdle EXECUTE(= -28874),
  90.         eReadInProgress,
  91.         eReadFinished
  92.     } SessionReadStates;
  93.  
  94. typedef enum
  95.     {
  96.         eSessionNormal EXECUTE(= -9887),
  97.         eSessionDataArrived,
  98.         eSessionClosed
  99.     } SessionStates;
  100.  
  101. struct SessionIDType
  102.     {
  103.         PPCSessRefNum                    SessionRefnum;
  104.         PortIDType*                        Port;
  105.         SessionWriteStates        WriteState;
  106.         SessionReadStates            ReadState;
  107.         SessionStates                    OverallState;
  108.         BufferRec*                        SendHead;
  109.         BufferRec*                        SendTail;
  110.         BufferRec*                        SendInProgress; /* valid if WriteState != eWriteIdle */
  111.         BufferRec*                        ReceiveHead;
  112.         BufferRec*                        ReceiveTail;
  113.         BufferRec*                        ReceiveInProgress; /* valid if ReadState != eReadIdle */
  114.         PPCReadPBRec                    ThePPCReadPBRec;
  115.         PPCWritePBRec                    ThePPCWritePBRec;
  116.         PPCPortRec                        RemotePortName; /* probably not needed */
  117.         LocationNameRec                RemoteLocationName; /* NBP name of remote machine */
  118.     };
  119.  
  120.  
  121. /* maintains the status of this module for debugging */
  122. EXECUTE(static MyBoolean                    Initialized = False;)
  123.  
  124. /* list of network ports.  NIL means the port slot is available.  the length */
  125. /* is determined by the heap block size. */
  126. static ArrayRec*                                    PortArray;
  127.  
  128. /* list of sessions.  NIL means session slot is available... */
  129. static ArrayRec*                                    SessionArray;
  130.  
  131. /* This system allows you to make an outgoing connection without a port.  The */
  132. /* Macintosh requires all connections to be made through ports, so we provide */
  133. /* a "system port" through which all outgoing connections are made. */
  134. static PortIDType*                                AppleTalkSystemPort;
  135.  
  136. /* session scan counter.  this is used so that all of the sessions are checked */
  137. /* fairly instead of favoring ones that are near the beginning of the list */
  138. static long                                                SessionScan;
  139.  
  140.  
  141. /* utility function prototypes */
  142. static void                    ReinstallPPCInform(PortIDType* Port);
  143. static MyBoolean        DecodeMachineName(char* MachineStr, char ObjStr[32],
  144.                                             char TypeStr[32], char ZoneStr[32]);
  145. static void                    UpdateRead(SessionIDType* Session);
  146. static void                    UpdateWrite(SessionIDType* Session);
  147. static pascal void    MyPPCInformCompletionRoutine(PPCInformPBRec* PB);
  148. static pascal void    MyPPCReadCompletionRoutine(PPCParamBlockRec* PB);
  149. static pascal void    MyPPCWriteCompletionRoutine(PPCParamBlockRec* PB);
  150.  
  151.  
  152. /* initialize the network interface.  the user calls this.  it is not called from */
  153. /* the standard Level 0 initialization routine because not all programs need */
  154. /* networking.  Could return eNetNoError, eNetNoMemory, eNetNetworkNotAvailable, */
  155. /* eNetCouldntInitNet, or eNetUnknownError */
  156. NetErrors                        InitializeNetwork(void)
  157.     {
  158.         OSErr                            Error;
  159.         long                            Result;
  160.         NetErrors                    ReturnValue;
  161.         unsigned long            PortIDScan;
  162.  
  163.         ERROR(Initialized,PRERR(ForceAbort,"InitializeNetwork:  already initialized"));
  164.         EXECUTE(Initialized = True;)
  165.         Error = Gestalt(gestaltPPCToolboxAttr,&Result);
  166.         if (Error != noErr)
  167.             {
  168.                 ReturnValue = eNetNetworkNotAvailable;
  169.              FailurePoint1:
  170.                 EXECUTE(Initialized = False;)
  171.                 return ReturnValue;
  172.             }
  173.         if ((Result & gestaltPPCSupportsRealTime) == 0)
  174.             {
  175.                 /* not initialized.  try initializing */
  176.                 Error = PPCInit();
  177.                 if (Error != noErr)
  178.                     {
  179.                         ReturnValue = eNetCouldntInitNet;
  180.                         goto FailurePoint1;
  181.                     }
  182.                 Error = Gestalt(gestaltPPCToolboxAttr,&Result);
  183.             }
  184.         /* don't know what to do in these cases */
  185.         if ((Result & gestaltPPCSupportsOutGoing) == 0)
  186.             {
  187.             }
  188.         if ((Result & gestaltPPCSupportsIncoming) == 0)
  189.             {
  190.             }
  191.         /* allocate arrays for holding session and port records */
  192.         PortArray = NewArray();
  193.         if (PortArray == NIL)
  194.             {
  195.                 ReturnValue = eNetNoMemory;
  196.              FailurePoint2:
  197.                 goto FailurePoint1;
  198.             }
  199.         SessionArray = NewArray();
  200.         if (SessionArray == NIL)
  201.             {
  202.                 ReturnValue = eNetNoMemory;
  203.              FailurePoint3:
  204.                 DisposeArray(PortArray);
  205.                 goto FailurePoint2;
  206.             }
  207.         /* initialize the session counter for NetUpdate */
  208.         SessionScan = 0;
  209.         /* trying to obtain a port for outgoing connections.  port names must be unique */
  210.         /* and since there may be several programs using this package running on the */
  211.         /* same computer, we need to search for an available port to work out of. */
  212.         for (PortIDScan = 0xffffffff; PortIDScan > 0; PortIDScan -= 1)
  213.             {
  214.                 char*                            PortNameTemp;
  215.                 int                                Scan;
  216.  
  217.                 PortNameTemp = AllocPtrCanFail(8,"SysPortName");
  218.                 if (PortNameTemp == NIL)
  219.                     {
  220.                         ReturnValue = eNetNoMemory;
  221.                         goto FailurePoint3;
  222.                     }
  223.                 for (Scan = 0; Scan < 8; Scan += 1)
  224.                     {
  225.                         PortNameTemp[Scan] = 'a' + ((PortIDScan >> (Scan * 4)) & 15);
  226.                     }
  227.                 /* try to open the port */
  228.                 ReturnValue = NetListenAtPort(PortNameTemp,&AppleTalkSystemPort,eNetAppleTalk);
  229.                 ReleasePtr(PortNameTemp);
  230.                 /* see if the port worked */
  231.                 switch (ReturnValue)
  232.                     {
  233.                         default:
  234.                             EXECUTE(PRERR(ForceAbort,
  235.                                 "InitializeNetwork:  bad value from NetListenAtPort"));
  236.                             break;
  237.                         case eNetNoError:
  238.                             goto SystemPortAllocatedPoint;
  239.                         case eNetNoMemory:
  240.                             /* value of ReturnValue stays the same */
  241.                             /* ReturnValue = eNetNoMemory; */
  242.                          FailurePoint4:
  243.                             DisposeArray(SessionArray);
  244.                             goto FailurePoint3;
  245.                         case eNetPortInUse:
  246.                             break; /* loop again */
  247.                         case eNetBadPortString:
  248.                             EXECUTE(PRERR(ForceAbort,"InitializeNetwork:  bad port string"));
  249.                             break;
  250.                         case eNetUnknownError:
  251.                             /* value of ReturnValue stays the same */
  252.                             /* ReturnValue = eNetUnknownError; */
  253.                             goto FailurePoint4;
  254.                             break;
  255.                     }
  256.             }
  257.         /* fall through:  couldn't allocate any system ports */
  258.         ReturnValue = eNetUnknownError;
  259.         goto FailurePoint4;
  260.         /* jump here if all is good */
  261.      SystemPortAllocatedPoint:
  262.         return eNetNoError;
  263.     }
  264.  
  265.  
  266. /* discard any pending data, close any open connections, and clean internal data */
  267. void                                ShutdownNetwork(void)
  268.     {
  269.         long                            Scan;
  270.         long                            Limit;
  271.  
  272.         ERROR(!Initialized,PRERR(ForceAbort,"ShutdownNetwork:  not initialized"));
  273.         /* first, close all sessions */
  274.         Limit = ArrayGetLength(SessionArray);
  275.         for (Scan = 0; Scan < Limit; Scan += 1)
  276.             {
  277.                 SessionIDType*        Session;
  278.  
  279.                 Session = (SessionIDType*)ArrayGetElement(SessionArray,Scan);
  280.                 EXECUTE(PRERR(AllowResume,"ShutdownNetwork:  session still open"));
  281.                 NetCloseSession(Session);
  282.             }
  283.         /* now close all open ports, including the system port */
  284.         NetTerminatePortAndSessions(AppleTalkSystemPort); /* technically not needed */
  285.         Limit = ArrayGetLength(PortArray);
  286.         for (Scan = 0; Scan < Limit; Scan += 1)
  287.             {
  288.                 PortIDType*                Port;
  289.  
  290.                 Port = (PortIDType*)ArrayGetElement(PortArray,Scan);
  291.                 EXECUTE(PRERR(AllowResume,"ShutdownNetwork:  found a port that's still open"));
  292.                 NetTerminatePortAndSessions(Port);
  293.             }
  294.         /* now release memory occupied by arrays */
  295.         DisposeArray(PortArray);
  296.         DisposeArray(SessionArray);
  297.         EXECUTE(Initialized = False;)
  298.     }
  299.  
  300.  
  301. /* Network update routine.  It handles periodic update tasks and should be called */
  302. /* frequently (preferably in the main event loop next to GetAnEvent). */
  303. NetEvents                        NetUpdate(SessionIDType** SessionNumber)
  304.     {
  305.         OSErr                            Error;
  306.         long                            Scan;
  307.         long                            Limit;
  308.         long                            OldSessionScan;
  309.         NetEvents                    ReturnValue;
  310.  
  311.         ERROR(SessionNumber == NIL,PRERR(ForceAbort,"NetUpdate:  SessionNumber == NIL"));
  312.         ERROR(!Initialized,PRERR(ForceAbort,"NetUpdate:  not initialized"));
  313.         /* look for PPCInforms that completed */
  314.         Limit = ArrayGetLength(PortArray);
  315.         for (Scan = 0; Scan < Limit; Scan += 1)
  316.             {
  317.                 PortIDType*                Port;
  318.  
  319.                 Port = (PortIDType*)ArrayGetElement(PortArray,Scan);
  320.                 if (Port->PortState == eConnectionPending)
  321.                     {
  322.                         SessionIDType*        Session;
  323.  
  324.                         /* return an event indicating the new session and reset the PPCInform */
  325.                         Session = (SessionIDType*)AllocPtrCanFail(sizeof(SessionIDType),"Session");
  326.                         if ((Session == NIL) || (Port == AppleTalkSystemPort))
  327.                             {
  328.                                 PPCEndPBRec                ThePPCEndPBRec;
  329.  
  330.                                 /* if we are out of memory, then kill the session */
  331.                                 /* we also do this if the connection was established on the */
  332.                                 /* system port */
  333.                              NewSessionFailurePoint1:
  334.                                 ThePPCEndPBRec.sessRefNum = Port->ThePPCInformPBRec.sessRefNum;
  335.                                 Error = PPCEnd(&ThePPCEndPBRec,False/*sync*/);
  336.                                 ERROR(Error != noErr,PRERR(AllowResume,"NetUpdate:  PPCEnd != noErr"));
  337.                                 ReinstallPPCInform(Port); /* this resets PortState */
  338.                                 goto NextPortPoint;
  339.                             }
  340.                         if (!ArrayAppendElement(SessionArray,Session))
  341.                             {
  342.                                 goto NewSessionFailurePoint1;
  343.                             }
  344.                         /* initialize all of the fields of the session record */
  345.                         Session->SessionRefnum = Port->ThePPCInformPBRec.sessRefNum;
  346.                         Session->Port = Port;
  347.                         Session->WriteState = eWriteIdle;
  348.                         Session->ReadState = eReadIdle;
  349.                         Session->OverallState = eSessionNormal;
  350.                         Session->SendHead = NIL;
  351.                         Session->SendTail = NIL;
  352.                         Session->SendInProgress = NIL;
  353.                         Session->ReceiveHead = NIL;
  354.                         Session->ReceiveTail = NIL;
  355.                         Session->ReceiveInProgress = NIL;
  356.                         /* initiate the first PPC read operation */
  357.                         UpdateRead(Session);
  358.                         /* copy over the port and location names of the remote session */
  359.                         Session->RemotePortName = Port->RemotePortName;
  360.                         Session->RemoteLocationName = Port->RemoteLocationName;
  361.                         ReinstallPPCInform(Port); /* this resets PortState */
  362.                         /* return session number for caller */
  363.                         *SessionNumber = Session;
  364.                         return eNetEventNewSession;
  365.                     }
  366.                 /* jump here after killing a session that we couldn't handle so that */
  367.                 /* we can check the next port. */
  368.              NextPortPoint:
  369.                 ;
  370.             }
  371.         /* now do session scans */
  372.         ReturnValue = eNetEventNone;
  373.         Limit = ArrayGetLength(SessionArray);
  374.         if (Limit > 0)
  375.             {
  376.                 OldSessionScan = SessionScan;
  377.                 if (OldSessionScan > Limit - 1)
  378.                     {
  379.                         /* this prevents us from getting stuck in an infinite loop if someone */
  380.                         /* removed an array element and left SessionScan beyond the end of the array */
  381.                         OldSessionScan = 0;
  382.                     }
  383.                 do
  384.                     {
  385.                         SessionIDType*            Session;
  386.  
  387.                         /* we increment first so that we don't dwell on hot sessions */
  388.                         SessionScan += 1;
  389.                         if (SessionScan >= Limit)
  390.                             {
  391.                                 SessionScan = 0;
  392.                             }
  393.                         Session = (SessionIDType*)ArrayGetElement(SessionArray,SessionScan);
  394.                         /* check to see if port is closed */
  395.                         if (Session->OverallState == eSessionClosed)
  396.                             {
  397.                                 *SessionNumber = Session;
  398.                                 return eNetEventSessionClosed;
  399.                             }
  400.                         /* check to see if write needs to be retriggered */
  401.                         if (Session->WriteState != eWriteInProgress)
  402.                             {
  403.                                 if (Session->WriteState == eWriteFinished)
  404.                                     {
  405.                                         ReturnValue = eNetEventInternal;
  406.                                     }
  407.                                 UpdateWrite(Session);
  408.                             }
  409.                         /* check to see if read needs to be retriggered */
  410.                         if (Session->ReadState != eReadInProgress)
  411.                             {
  412.                                 if (Session->ReadState == eReadFinished)
  413.                                     {
  414.                                         ReturnValue = eNetEventInternal;
  415.                                     }
  416.                                 UpdateRead(Session);
  417.                             }
  418.                         /* check to see if there is new data */
  419.                         if (Session->OverallState == eSessionDataArrived)
  420.                             {
  421.                                 Session->OverallState = eSessionNormal;
  422.                                 *SessionNumber = Session;
  423.                                 return eNetEventDataIncoming;
  424.                             }
  425.                     } while (SessionScan != OldSessionScan);
  426.             }
  427.         return ReturnValue;
  428.     }
  429.  
  430.  
  431. /* local utility routine for installing an asynchronous PPC callback */
  432. static void                    ReinstallPPCInform(PortIDType* Port)
  433.     {
  434.         OSErr                            Error;
  435.  
  436.         Port->ThePPCInformPBRec.ioCompletion = NewPPCCompProc(&MyPPCInformCompletionRoutine);
  437.         Port->ThePPCInformPBRec.portRefNum = Port->ThePortRefnum;
  438.         Port->ThePPCInformPBRec.autoAccept = True;
  439.         /* provide a place for identifying the remote port */
  440.         Port->ThePPCInformPBRec.portName = &(Port->RemotePortName);
  441.         /* provide a place for identifying the remote machine */
  442.         Port->ThePPCInformPBRec.locationName = &(Port->RemoteLocationName);
  443.         /* we don't support user names */
  444.         Port->ThePPCInformPBRec.userName = NIL;
  445.         Port->PortState = eAwaitingConnection; /* BEFORE the call */
  446.         Error = PPCInform(&(Port->ThePPCInformPBRec),True/*async*/);
  447.         ERROR(Error != noErr,PRERR(AllowResume,"ReinstallPPCInform:  PPCInform != noErr"));
  448.     }
  449.  
  450.  
  451. /* Listen at the specified port.  Returns the reference number of the port being */
  452. /* listened at.  PortString is a non-null-terminated heap block containing the string */
  453. /* identifying the port.  The port string format depends on the underlying network */
  454. /* scheme.  For instance, TCP/IP would be an integer between 128 and 9999. */
  455. /* for AppleTalk (NBP), it is a valid NBP name.  Could return eNetNoError, */
  456. /* eNetNoMemory, eNetPortInUse, eNetBadPortString, eNetUnknownError, or */
  457. /* eNetProtocolNotSupported. */
  458. NetErrors                        NetListenAtPort(char* PortString, PortIDType** PortOut,
  459.                                             NetworkTypes WhichNetwork)
  460.     {
  461.         PPCOpenPBRec            ThePPCOpenPBRec;
  462.         long                            PortStringLength;
  463.         PortIDType*                Port;
  464.         OSErr                            Error;
  465.         NetErrors                    ReturnValue;
  466.  
  467.         ERROR(!Initialized,PRERR(ForceAbort,"NetListenAtPort:  not initialized"));
  468.         CheckPtrExistence(PortString);
  469.         ERROR(PortOut == NIL,PRERR(ForceAbort,"NetListenAtPort:  PortOut is NIL"));
  470.         /* verify network connection requested */
  471.         switch (WhichNetwork)
  472.             {
  473.                 case eNetDefault:
  474.                 case eNetAppleTalk:
  475.                     break;
  476.                 case eNetTCP:
  477.                     return eNetProtocolNotSupported;
  478.                 default:
  479.                     EXECUTE(PRERR(ForceAbort,"NetListenAtPort:  bad protocol specified"));
  480.                     break;
  481.             }
  482.         /* make a place for it */
  483.         Port = (PortIDType*)AllocPtrCanFail(sizeof(PortIDType),"PortIDType");
  484.         if (Port == NIL)
  485.             {
  486.                 ReturnValue = eNetNoMemory;
  487.              FailurePoint1:
  488.                 return ReturnValue;
  489.             }
  490.         if (!ArrayAppendElement(PortArray,Port))
  491.             {
  492.                 ReturnValue = eNetNoMemory;
  493.              FailurePoint2:
  494.                 ReleasePtr((char*)Port);
  495.                 goto FailurePoint1;
  496.             }
  497.         /* initialize the port record */
  498.         Port->ThePPCPortRec.nameScript = smRoman;
  499.         PortStringLength = PtrSize(PortString);
  500.         if (PortStringLength > 31)
  501.             {
  502.                 ReturnValue = eNetBadPortString;
  503.              FailurePoint3:
  504.                 ArrayDeleteElement(PortArray,ArrayFindElement(PortArray,Port));
  505.                 goto FailurePoint2;
  506.             }
  507.         CopyData(PortString,(char*)&(Port->ThePPCPortRec.name[1]),PortStringLength);
  508.         Port->ThePPCPortRec.name[0] = PortStringLength; /* stupid pascal strings */
  509.         Port->ThePPCPortRec.portKindSelector = ppcByString;
  510.         CopyData(DEFAULTTYPE,(char*)&(Port->ThePPCPortRec.u.portTypeStr[1]),
  511.             DEFAULTTYPELENGTH);
  512.         Port->ThePPCPortRec.u.portTypeStr[0] = DEFAULTTYPELENGTH;
  513.         /* initialize the Openrec */
  514.         ThePPCOpenPBRec.serviceType = ppcServiceRealTime;
  515.         ThePPCOpenPBRec.resFlag = 0;
  516.         ThePPCOpenPBRec.portName = &(Port->ThePPCPortRec);
  517.         ThePPCOpenPBRec.locationName = NIL;
  518.         ThePPCOpenPBRec.networkVisible = True;
  519.         /* try to open it */
  520.         Error = PPCOpen(&ThePPCOpenPBRec,False/*synchronous*/);
  521.         switch (Error)
  522.             {
  523.                 case noErr:
  524.                     Port->ThePortRefnum = ThePPCOpenPBRec.portRefNum;
  525.                     break; /* continue on to the next phase */
  526.                 case badLocNameErr:
  527.                     ReturnValue = eNetBadPortString;
  528.                     goto FailurePoint3;
  529.                 case portNameExistsErr:
  530.                 case nbpDuplicate:
  531.                     ReturnValue = eNetPortInUse;
  532.                     goto FailurePoint3;
  533.                 default:
  534.                     ReturnValue = eNetUnknownError;
  535.                     goto FailurePoint3;
  536.             }
  537.         /* indicate that the callback is out there */
  538.         Port->PortState = eAwaitingConnection;
  539.         /* enable listen callback */
  540.         ReinstallPPCInform(Port);
  541.         /* put reference number out for caller */
  542.         *PortOut = Port;
  543.         return eNetNoError;
  544.     }
  545.  
  546.  
  547. /* Stop listening at a port and let the OS use it for someone else.  Sessions */
  548. /* established through this port are terminated. */
  549. void                                NetTerminatePortAndSessions(PortIDType* Port)
  550.     {
  551.         OSErr                            Error;
  552.         PPCClosePBRec            TheCloseRec;
  553.         long                            Scan;
  554.         long                            Limit;
  555.  
  556.         ERROR(!Initialized,PRERR(ForceAbort,
  557.             "NetTerminatePortAndSessions:  not initialized"));
  558.         CheckPtrExistence(Port);
  559.         /* close any sessions registered through this port */
  560.         Limit = ArrayGetLength(SessionArray);
  561.         Scan = 0;
  562.         while (Scan < Limit)
  563.             {
  564.                 SessionIDType*            Session;
  565.  
  566.                 Session = (SessionIDType*)ArrayGetElement(SessionArray,Scan);
  567.                 if (Session->Port == Port)
  568.                     {
  569.                         /* clean up our local data structures */
  570.                         NetCloseSession(Session);
  571.                         /* decrement limit, since we just dropped current element from array */
  572.                         Limit -= 1;
  573.                     }
  574.                  else
  575.                     {
  576.                         /* only increment this if we didn't delete the element */
  577.                         Scan += 1;
  578.                     }
  579.             }
  580.         /* close the port itself */
  581.         TheCloseRec.portRefNum = Port->ThePortRefnum;
  582.         Error = PPCClose(&TheCloseRec,False/*synchronous*/);
  583.         ERROR(Error != noErr,PRERR(AllowResume,
  584.             "NetTerminatePortAndSessions:  return from PPCClose != noErr"));
  585.         ArrayDeleteElement(PortArray,ArrayFindElement(PortArray,Port));
  586.         ReleasePtr((char*)Port);
  587.     }
  588.  
  589.  
  590. /* Open a session to another (or the same) machine.  Returns a session number in */
  591. /* *SessionOut.  PortString is the remote port to connect to, as described above, */
  592. /* and MachineString is a machine string determined by the network stack.  For */
  593. /* instance, TCP/IP would support standard a.b.c.d or machine.zone.domain format. */
  594. /* The Macintosh uses machine:type@zone.  Could return eNetNoError, eNetNoMemory, */
  595. /* eNetBadPortString, eNetBadMachineString, eNetMachineUnknown, eNetUnknownError, */
  596. /* eNetConnectRefused, or eNetProtocolNotSupported. */
  597. NetErrors                        NetOpenSession(char* PortString, char* MachineString,
  598.                                             SessionIDType** SessionOut, NetworkTypes WhichNetwork)
  599.     {
  600.         PPCStartPBRec            ThePPCStartPBRec;
  601.         OSErr                            Error;
  602.         long                            PortNameLength;
  603.         SessionIDType*        Session;
  604.         NetErrors                    ReturnValue;
  605.         long                            TryCounter;
  606.         unsigned char            UserNamePlace[32];
  607.  
  608.         ERROR(!Initialized,PRERR(ForceAbort,"NetOpenSession:  not initialized"));
  609.         CheckPtrExistence(PortString);
  610.         CheckPtrExistence(MachineString);
  611.         ERROR(SessionOut == NIL,PRERR(ForceAbort,"NetOpenSession:  SessionOut == NIL"));
  612.         /* verify network connection requested */
  613.         switch (WhichNetwork)
  614.             {
  615.                 case eNetDefault:
  616.                 case eNetAppleTalk:
  617.                     break;
  618.                 case eNetTCP:
  619.                     return eNetProtocolNotSupported;
  620.                 default:
  621.                     EXECUTE(PRERR(ForceAbort,"NetListenAtPort:  bad protocol specified"));
  622.                     break;
  623.             }
  624.         /* allocate a place for it */
  625.         Session = (SessionIDType*)AllocPtrCanFail(sizeof(SessionIDType),"SessionIDType");
  626.         if (Session == NIL)
  627.             {
  628.                 ReturnValue = eNetNoMemory;
  629.              FailurePoint1:
  630.                 return ReturnValue;
  631.             }
  632.         if (!ArrayAppendElement(SessionArray,Session))
  633.             {
  634.                 ReturnValue = eNetNoMemory;
  635.              FailurePoint2:
  636.                 ReleasePtr((char*)Session);
  637.                 goto FailurePoint1;
  638.             }
  639.         /* initialize the ppc record */
  640.         EXECUTE(ThePPCStartPBRec.ioCompletion = NewPPCCompProc((void*)0x81818181);)
  641.         ThePPCStartPBRec.portRefNum = AppleTalkSystemPort->ThePortRefnum;
  642.         ThePPCStartPBRec.serviceType = ppcServiceRealTime;
  643.         ThePPCStartPBRec.resFlag = 0;
  644.         /* see if this connection is for this machine or another */
  645.         PortNameLength = PtrSize(PortString);
  646.         if (PortNameLength > 31)
  647.             {
  648.                 ReturnValue = eNetBadPortString;
  649.              FailurePoint3:
  650.                 ArrayDeleteElement(SessionArray,ArrayFindElement(SessionArray,Session));
  651.                 goto FailurePoint2;
  652.             }
  653.         CopyData(PortString,(char*)&(Session->RemotePortName.name[1]),PortNameLength);
  654.         Session->RemotePortName.name[0] = PortNameLength;
  655.         Session->RemotePortName.nameScript = smRoman;
  656.         Session->RemotePortName.portKindSelector = ppcByString;
  657.         CopyData(DEFAULTTYPE,(char*)&(Session->RemotePortName.u.portTypeStr[1]),
  658.             DEFAULTTYPELENGTH);
  659.         Session->RemotePortName.u.portTypeStr[0] = DEFAULTTYPELENGTH;
  660.         ThePPCStartPBRec.portName = &(Session->RemotePortName);
  661.         if ((PtrSize(MachineString) != 9) && (PtrSize(MachineString) != 0))
  662.             {
  663.                 char                            MyNBPObjectStr[32];
  664.                 char                            MyNBPTypeStr[32];
  665.                 char                            MyNBPZoneStr[32];
  666.  
  667.                 /* remote machine specified */
  668.              RemoteName:
  669.                 if (!DecodeMachineName(MachineString,MyNBPObjectStr,MyNBPTypeStr,MyNBPZoneStr))
  670.                     {
  671.                         ReturnValue = eNetBadMachineString;
  672.                         goto FailurePoint2;
  673.                     }
  674.                 CopyData(&(MyNBPObjectStr[0]),(char*)&(Session->RemoteLocationName
  675.                     .u.nbpEntity.objStr),MyNBPObjectStr[0] + 1);
  676.                 CopyData(&(MyNBPTypeStr[0]),(char*)&(Session->RemoteLocationName
  677.                     .u.nbpEntity.typeStr),MyNBPTypeStr[0] + 1);
  678.                 CopyData(&(MyNBPZoneStr[0]),(char*)&(Session->RemoteLocationName
  679.                     .u.nbpEntity.zoneStr),MyNBPZoneStr[0] + 1);
  680.                 Session->RemoteLocationName.locationKindSelector = ppcNBPLocation;
  681.                 ThePPCStartPBRec.locationName = &(Session->RemoteLocationName);
  682.             }
  683.          else
  684.             {
  685.                 if (PtrSize(MachineString) == 9)
  686.                     {
  687.                         long                            Scan;
  688.  
  689.                         for (Scan = 0; Scan < 9; Scan += 1)
  690.                             {
  691.                                 if ("localhost"[Scan] != MachineString[Scan])
  692.                                     {
  693.                                         goto RemoteName;
  694.                                     }
  695.                             }
  696.                     }
  697.                 /* it's the local machine */
  698.                 Session->RemoteLocationName.locationKindSelector = ppcNoLocation;
  699.                 ThePPCStartPBRec.locationName = &(Session->RemoteLocationName);
  700.             }
  701.         ThePPCStartPBRec.userData = 0; /* users are not used */
  702.         if (noErr != GetDefaultUser(&(ThePPCStartPBRec.userRefNum),UserNamePlace))
  703.             {
  704.                 ThePPCStartPBRec.userRefNum = 0;
  705.             }
  706.         TryCounter = MAXNUMTRIES;
  707.      TryAgainPoint:
  708.         Error = PPCStart(&ThePPCStartPBRec,False/*sync*/);
  709.         switch (Error)
  710.             {
  711.                 default:
  712.                     ReturnValue = eNetUnknownError;
  713.                     goto FailurePoint3;
  714.                 case destPortErr:
  715.                 case noResponseErr:
  716.                 case userRejectErr:
  717.                 case localOnlyErr:
  718.                 case guestNotAllowedErr:
  719.                     ReturnValue = eNetConnectRefused;
  720.                     goto FailurePoint3;
  721.                 case noErr:
  722.                     break; /* continue */
  723.                 case noInformErr:
  724.                     TryCounter -= 1;
  725.                     /* sleep a little bit */
  726.                     {
  727.                         double            Now;
  728.  
  729.                         Now = ReadTimer();
  730.                         while (TimerDifference(ReadTimer(),Now) < TRYDELAY)
  731.                             {
  732.                                 RelinquishCPUCheckCancel();
  733.                             }
  734.                     }
  735.                     /* see if we should try again */
  736.                     if (TryCounter > 0)
  737.                         {
  738.                             goto TryAgainPoint;
  739.                         }
  740.                     ReturnValue = eNetConnectRefused;
  741.                     goto FailurePoint3;
  742.                 case portClosedErr:
  743.                 case badPortNameErr:
  744.                 case noUserRecErr:
  745.                 case noPortErr:
  746.                 case badServiceMethodErr:
  747.                 case nameTypeErr:
  748.                     EXECUTE(PRERR(ForceAbort,"NetOpenSession:  bad error code"));
  749.                     break;
  750.             }
  751.         /* initialize all of the structure's parameters */
  752.         Session->SessionRefnum = ThePPCStartPBRec.sessRefNum;
  753.         Session->Port = AppleTalkSystemPort;
  754.         Session->WriteState = eWriteIdle;
  755.         Session->ReadState = eReadIdle;
  756.         Session->OverallState = eSessionNormal;
  757.         Session->SendHead = NIL;
  758.         Session->SendTail = NIL;
  759.         Session->SendInProgress = NIL;
  760.         Session->ReceiveHead = NIL;
  761.         Session->ReceiveTail = NIL;
  762.         Session->ReceiveInProgress = NIL;
  763.         *SessionOut = Session;
  764.         return eNetNoError;
  765.     }
  766.  
  767.  
  768. /* utility routine to decode string of format <name>:<type>@<zone> */
  769. /* we allow the omission of <type> and automatically substitute DEFAULTTYPE for it */
  770. /* you can omit the zone name as well. */
  771. static MyBoolean        DecodeMachineName(char* MachineStr, char ObjStr[32],
  772.                                             char TypeStr[32], char ZoneStr[32])
  773.     {
  774.         long                            Limit;
  775.         long                            Scan;
  776.  
  777.         Limit = PtrSize(MachineStr);
  778.         Scan = 0;
  779.         while ((Scan < Limit) && (MachineStr[Scan] != ':') && (MachineStr[Scan] != '@'))
  780.             {
  781.                 if (Scan >= 31)
  782.                     {
  783.                         return False;
  784.                     }
  785.                 ObjStr[Scan + 1] = MachineStr[Scan];
  786.                 Scan += 1;
  787.             }
  788.         ObjStr[0] = Scan;
  789.         if ((Scan == Limit) || (MachineStr[Scan] == '@'))
  790.             {
  791.                 /* type name omitted */
  792.                 CopyData(DEFAULTTYPE,&(TypeStr[1]),DEFAULTTYPELENGTH);
  793.                 TypeStr[0] = DEFAULTTYPELENGTH;
  794.                 goto ElidedTypePoint;
  795.             }
  796.         MachineStr += Scan + 1;
  797.         Limit -= Scan + 1;
  798.         Scan = 0;
  799.         while ((Scan < Limit) && (MachineStr[Scan] != '@'))
  800.             {
  801.                 if (Scan >= 31)
  802.                     {
  803.                         return False;
  804.                     }
  805.                 TypeStr[Scan + 1] = MachineStr[Scan];
  806.                 Scan += 1;
  807.             }
  808.         TypeStr[0] = Scan;
  809.      ElidedTypePoint:
  810.         if (Scan == Limit)
  811.             {
  812.                 /* no zone name */
  813.                 ZoneStr[0] = 0;
  814.                 return True;
  815.             }
  816.         MachineStr += Scan + 1;
  817.         Limit -= Scan + 1;
  818.         Scan = 0;
  819.         while (Scan < Limit)
  820.             {
  821.                 if (Scan >= 31)
  822.                     {
  823.                         return False;
  824.                     }
  825.                 ZoneStr[Scan + 1] = MachineStr[Scan];
  826.                 Scan += 1;
  827.             }
  828.         ZoneStr[0] = Scan;
  829.         return True;
  830.     }
  831.  
  832.  
  833. /* Close a session.  Any waiting data in either direction is discarded. */
  834. void                                NetCloseSession(SessionIDType* Session)
  835.     {
  836.         OSErr                            Error;
  837.         PPCEndPBRec                EndRec;
  838.         BufferRec*                BuffScan;
  839.  
  840.         ERROR(!Initialized,PRERR(ForceAbort,"NetCloseSession:  not initialized"));
  841.         CheckPtrExistence(Session);
  842.         /* first, kill the session to complete any reads/writes in progress. */
  843.         /* this might fail if the session is already dead. */
  844.         EndRec.sessRefNum = Session->SessionRefnum;
  845.         Error = PPCEnd(&EndRec,False/*sync*/);
  846.         /* now that that's finished, we can dump the buffers */
  847.         if (Session->ReceiveInProgress != NIL)
  848.             {
  849.                 ReleasePtr((char*)(Session->ReceiveInProgress));
  850.             }
  851.         if (Session->SendInProgress != NIL)
  852.             {
  853.                 ReleasePtr((char*)(Session->SendInProgress));
  854.             }
  855.         BuffScan = Session->SendHead;
  856.         while (BuffScan != NIL)
  857.             {
  858.                 BufferRec*                BuffTemp;
  859.  
  860.                 BuffTemp = BuffScan;
  861.                 BuffScan = BuffScan->Next;
  862.                 ReleasePtr((char*)BuffTemp);
  863.             }
  864.         BuffScan = Session->ReceiveHead;
  865.         while (BuffScan != NIL)
  866.             {
  867.                 BufferRec*                BuffTemp;
  868.  
  869.                 BuffTemp = BuffScan;
  870.                 BuffScan = BuffScan->Next;
  871.                 ReleasePtr((char*)BuffTemp);
  872.             }
  873.         /* now dump the session record */
  874.         ArrayDeleteElement(SessionArray,ArrayFindElement(SessionArray,Session));
  875.         ReleasePtr((char*)Session);
  876.     }
  877.  
  878.  
  879. /* utility routine to submit a new read operation if one has completed */
  880. static void                    UpdateRead(SessionIDType* Session)
  881.     {
  882.         OSErr                            Error;
  883.  
  884.         CheckPtrExistence(Session);
  885.         ERROR(Session->ReadState == eReadInProgress,PRERR(ForceAbort,
  886.             "UpdateRead:  read is already in progress"));
  887.         /* handle the data that comes back from a read operation. */
  888.         if (Session->ReadState == eReadFinished)
  889.             {
  890.                 CheckPtrExistence(Session->ReceiveInProgress);
  891.                 /* check for session closed notification */
  892.                 if (Session->ThePPCReadPBRec.ioResult == sessClosedErr)
  893.                     {
  894.                         Session->OverallState = eSessionClosed;
  895.                     }
  896.                 ERROR((Session->ThePPCReadPBRec.ioResult != sessClosedErr)
  897.                     && (Session->ThePPCReadPBRec.ioResult != noErr),PRERR(AllowResume,
  898.                     "UpdateRead:  error returned from PPCRead"));
  899.                 /* get read length */
  900.                 Session->ReceiveInProgress->NumBytes = Session->ThePPCReadPBRec.actualLength;
  901.                 ERROR((Session->ReceiveInProgress->NumBytes < 0)
  902.                     || (Session->ReceiveInProgress->NumBytes > BUFFERSIZE),PRERR(ForceAbort,
  903.                     "UpdateRead:  too many bytes returned from PPCRead"));
  904.                 if (Session->ReceiveInProgress->NumBytes != 0)
  905.                     {
  906.                         /* move this buffer onto the list */
  907.                         Session->ReceiveInProgress->Next = NIL;
  908.                         if (Session->ReceiveTail != NIL)
  909.                             {
  910.                                 CheckPtrExistence(Session->ReceiveTail);
  911.                                 Session->ReceiveTail->Next = Session->ReceiveInProgress;
  912.                             }
  913.                          else
  914.                             {
  915.                                 Session->ReceiveHead = Session->ReceiveInProgress;
  916.                             }
  917.                         Session->ReceiveTail = Session->ReceiveInProgress;
  918.                     }
  919.                  else
  920.                     {
  921.                         ReleasePtr((char*)(Session->ReceiveInProgress));
  922.                     }
  923.                 /* clear buffer register */
  924.                 Session->ReceiveInProgress = NIL;
  925.                 /* indicate that data has arrived */
  926.                 if (Session->OverallState == eSessionNormal)
  927.                     {
  928.                         /* don't change this if it contains eSessionClosed! */
  929.                         Session->OverallState = eSessionDataArrived;
  930.                     }
  931.                 /* reset read state */
  932.                 Session->ReadState = eReadIdle;
  933.             }
  934.         /* if the session has closed with the previous read, don't retrigger the read */
  935.         /* but instead just exit */
  936.         if (Session->OverallState == eSessionClosed)
  937.             {
  938.                 return;
  939.             }
  940.         /* retrigger the read operation on a new block */
  941.         Session->ReceiveInProgress = (BufferRec*)AllocPtrCanFail(
  942.             sizeof(BufferRec),"ReadBufferRec");
  943.         if (Session->ReceiveInProgress == NIL)
  944.             {
  945.                 return;
  946.             }
  947.         Session->ThePPCReadPBRec.ioCompletion = NewPPCCompProc(&MyPPCReadCompletionRoutine);
  948.         Session->ThePPCReadPBRec.sessRefNum = Session->SessionRefnum;
  949.         Session->ThePPCReadPBRec.bufferLength = BUFFERSIZE;
  950.         Session->ThePPCReadPBRec.bufferPtr = &(Session->ReceiveInProgress->Buffer[0]);
  951.         Session->ReadState = eReadInProgress; /* BEFORE the call! */
  952.         Error = PPCRead(&(Session->ThePPCReadPBRec),True/*async*/);
  953.     }
  954.  
  955.  
  956. /* utility routine to submit another write if one has completed */
  957. static void                    UpdateWrite(SessionIDType* Session)
  958.     {
  959.         OSErr                            Error;
  960.  
  961.         CheckPtrExistence(Session);
  962.         ERROR((Session->WriteState == eWriteInProgress),PRERR(ForceAbort,
  963.             "UpdateWrite:  write is already in progress"));
  964.         /* if a write has completed, then handle it */
  965.         if (Session->WriteState == eWriteFinished)
  966.             {
  967.                 /* check for session closed notification */
  968.                 if (Session->ThePPCWritePBRec.ioResult == sessClosedErr)
  969.                     {
  970.                         Session->OverallState = eSessionClosed;
  971.                     }
  972.                 ERROR((Session->ThePPCWritePBRec.ioResult != sessClosedErr)
  973.                     && (Session->ThePPCWritePBRec.ioResult != noErr),PRERR(AllowResume,
  974.                     "UpdateWrite:  error returned from PPCWrite"));
  975.                 /* reset write state stuff */
  976.                 Session->WriteState = eWriteIdle;
  977.                 /* dispose of the write buffer block */
  978.                 ReleasePtr((char*)(Session->SendInProgress));
  979.                 Session->SendInProgress = NIL;
  980.             }
  981.         /* if there is more data queued to be sent, then send it */
  982.         if (Session->SendHead != NIL)
  983.             {
  984.                 CheckPtrExistence(Session->SendHead);
  985.                 /* move the first block to the in progress register */
  986.                 Session->SendInProgress = Session->SendHead;
  987.                 /* pop the block off the queue */
  988.                 Session->SendHead = Session->SendHead->Next;
  989.                 if (Session->SendHead == NIL)
  990.                     {
  991.                         Session->SendTail = NIL;
  992.                     }
  993.                 /* construct the parameter record */
  994.                 Session->ThePPCWritePBRec.ioCompletion = NewPPCCompProc(&MyPPCWriteCompletionRoutine);
  995.                 Session->ThePPCWritePBRec.sessRefNum = Session->SessionRefnum;
  996.                 Session->ThePPCWritePBRec.bufferLength = Session->SendInProgress->NumBytes;
  997.                 Session->ThePPCWritePBRec.bufferPtr = &(Session->SendInProgress->Buffer[0]);
  998.                 Session->ThePPCWritePBRec.more = False; /* always false */
  999.                 Session->ThePPCWritePBRec.userData = 0;
  1000.                 Session->ThePPCWritePBRec.blockCreator = 0;
  1001.                 Session->ThePPCWritePBRec.blockType = 0;
  1002.                 Session->WriteState = eWriteInProgress; /* BEFORE the call! */
  1003.                 Error = PPCWrite(&(Session->ThePPCWritePBRec),True/*async*/);
  1004.             }
  1005.     }
  1006.  
  1007.  
  1008. /* Verify that a session is still usable.  Returns True if the session is still */
  1009. /* available, or False if the remote system disconnected it.  If it returns False, */
  1010. /* then you should call NetCloseSession to dispose of the session record. */
  1011. MyBoolean                        NetIsSessionStillAlive(SessionIDType* Session)
  1012.     {
  1013.         ERROR(!Initialized,PRERR(ForceAbort,"NetIsSessionStillAlive:  not initialized"));
  1014.         CheckPtrExistence(Session);
  1015.         return (Session->OverallState != eSessionClosed);
  1016.     }
  1017.  
  1018.  
  1019. /* Obtain a string identifying the machine from which a session has been */
  1020. /* established.  The string is a non-null-terminated heap block. */
  1021. char*                                NetSessionGetRemoteMachineName(SessionIDType* Session)
  1022.     {
  1023.         char                            LocalBuffer[32 + 32 + 32 + 1 + 1];
  1024.         long                            Index;
  1025.         long                            Scan;
  1026.         char*                            Pointer;
  1027.  
  1028.         ERROR(!Initialized,PRERR(ForceAbort,
  1029.             "NetSessionGetRemoteMachineName:  not initialized"));
  1030.         CheckPtrExistence(Session);
  1031.         switch (Session->RemoteLocationName.locationKindSelector)
  1032.             {
  1033.                 default:
  1034.                     EXECUTE(PRERR(ForceAbort,
  1035.                         "NetSessionGetRemoteMachineName:  unknown locationKindSelector"));
  1036.                     break;
  1037.                 case ppcNoLocation:
  1038.                     Index = 9;
  1039.                     CopyData("localhost",LocalBuffer,9);
  1040.                     break;
  1041.                 case ppcNBPLocation:
  1042.                     Index = 0;
  1043.                     for (Scan = 0; Scan < Session->RemoteLocationName
  1044.                         .u.nbpEntity.objStr[0]; Scan += 1)
  1045.                         {
  1046.                             LocalBuffer[Index++] = Session->RemoteLocationName
  1047.                                 .u.nbpEntity.objStr[Scan + 1];
  1048.                         }
  1049.                     if (Session->RemoteLocationName.u.nbpEntity.typeStr[0] != 0)
  1050.                         {
  1051.                             LocalBuffer[Index++] = ':';
  1052.                         }
  1053.                     for (Scan = 0; Scan < Session->RemoteLocationName
  1054.                         .u.nbpEntity.typeStr[0]; Scan += 1)
  1055.                         {
  1056.                             LocalBuffer[Index++] = Session->RemoteLocationName
  1057.                                 .u.nbpEntity.typeStr[Scan + 1];
  1058.                         }
  1059.                     if (Session->RemoteLocationName.u.nbpEntity.zoneStr[0] != 0)
  1060.                         {
  1061.                             LocalBuffer[Index++] = '@';
  1062.                         }
  1063.                     for (Scan = 0; Scan < Session->RemoteLocationName
  1064.                         .u.nbpEntity.zoneStr[0]; Scan += 1)
  1065.                         {
  1066.                             LocalBuffer[Index++] = Session->RemoteLocationName
  1067.                                 .u.nbpEntity.zoneStr[Scan + 1];
  1068.                         }
  1069.                     break;
  1070.             }
  1071.         Pointer = AllocPtrCanFail(Index,"NBPNameString");
  1072.         if (Pointer != NIL)
  1073.             {
  1074.                 CopyData(&(LocalBuffer[0]),&(Pointer[0]),Index);
  1075.             }
  1076.         return Pointer;
  1077.     }
  1078.  
  1079.  
  1080. /* Find out how much data is waiting to be read from the port. */
  1081. long                                NetHowMuchDataToRead(SessionIDType* Session)
  1082.     {
  1083.         long                            DataCount;
  1084.         BufferRec*                BufferScan;
  1085.  
  1086.         ERROR(!Initialized,PRERR(ForceAbort,"NetHowMuchDataToRead:  not initialized"));
  1087.         CheckPtrExistence(Session);
  1088.         BufferScan = Session->ReceiveHead;
  1089.         DataCount = 0;
  1090.         while (BufferScan != NIL)
  1091.             {
  1092.                 DataCount += BufferScan->NumBytes;
  1093.                 BufferScan = BufferScan->Next;
  1094.             }
  1095.         return DataCount;
  1096.     }
  1097.  
  1098.  
  1099. /* Find out how much data is waiting in local buffers to be written to a port. */
  1100. long                                NetHowMuchDataToWrite(SessionIDType* Session)
  1101.     {
  1102.         long                            DataCount;
  1103.         BufferRec*                BufferScan;
  1104.  
  1105.         ERROR(!Initialized,PRERR(ForceAbort,"NetHowMuchDataToWrite:  not initialized"));
  1106.         CheckPtrExistence(Session);
  1107.         /* initiate another read if there isn't one pending */
  1108.         if (Session->ReadState == eReadIdle)
  1109.             {
  1110.                 UpdateRead(Session);
  1111.             }
  1112.         /* count data in buffers waiting to be eaten */
  1113.         BufferScan = Session->SendHead;
  1114.         DataCount = 0;
  1115.         while (BufferScan != NIL)
  1116.             {
  1117.                 DataCount += BufferScan->NumBytes;
  1118.                 BufferScan = BufferScan->Next;
  1119.             }
  1120.         return DataCount;
  1121.     }
  1122.  
  1123.  
  1124. /* Read data from a session.  It is an error to read more data than there is waiting. */
  1125. void                                NetReadData(SessionIDType* Session, char* Buffer, long NumBytes)
  1126.     {
  1127.         ERROR(!Initialized,PRERR(ForceAbort,"NetReadData:  not initialized"));
  1128.         CheckPtrExistence(Session);
  1129.         ERROR((NumBytes < 0) || (NumBytes > NetHowMuchDataToRead(Session)),
  1130.             PRERR(ForceAbort,"NetReadData:  number of bytes is out of range"));
  1131.         /* extract data from blocks */
  1132.         while (NumBytes > 0)
  1133.             {
  1134.                 long                            TempNumBytes;
  1135.                 BufferRec*                TempBuffer;
  1136.  
  1137.                 TempBuffer = Session->ReceiveHead;
  1138.                 CheckPtrExistence(TempBuffer);
  1139.                 TempNumBytes = TempBuffer->NumBytes;
  1140.                 CopyData(&(TempBuffer->Buffer[0]),&(Buffer[0]),TempNumBytes);
  1141.                 Session->ReceiveHead = TempBuffer->Next;
  1142.                 NumBytes -= TempNumBytes;
  1143.                 Buffer += TempNumBytes;
  1144.                 ReleasePtr((char*)TempBuffer);
  1145.             }
  1146.         if (Session->ReceiveHead == NIL)
  1147.             {
  1148.                 Session->ReceiveTail = NIL;
  1149.             }
  1150.         /* initiate another read if there isn't one pending */
  1151.         if (Session->ReadState == eReadIdle)
  1152.             {
  1153.                 UpdateRead(Session);
  1154.             }
  1155.     }
  1156.  
  1157.  
  1158. /* Write data to a session.  If data could not be sent without blocking, then */
  1159. /* it is locally buffered until it can be sent (that's what NetUpdate is for) */
  1160. /* returns True if successful, or False if there isn't enough memory.  if it fails, */
  1161. /* then NO data is written (i.e. data is never partially written) */
  1162. MyBoolean                        NetWriteData(SessionIDType* Session, char* Buffer, long NumBytes)
  1163.     {
  1164.         BufferRec*                LocalHead;
  1165.         BufferRec*                LocalTail;
  1166.         BufferRec*                BuffScan;
  1167.         long                            TempByteCount;
  1168.  
  1169.         ERROR(!Initialized,PRERR(ForceAbort,"NetWriteData:  not initialized"));
  1170.         CheckPtrExistence(Session);
  1171.         /* we create the buffers totally first, and only submit them if we could */
  1172.         /* allocate ALL of them */
  1173.         LocalHead = NIL;
  1174.         LocalTail = NIL;
  1175.         TempByteCount = NumBytes;
  1176.         while (TempByteCount > 0)
  1177.             {
  1178.                 BufferRec*                TempBuff;
  1179.  
  1180.                 if (TempByteCount > BUFFERSIZE)
  1181.                     {
  1182.                         TempByteCount -= BUFFERSIZE;
  1183.                     }
  1184.                  else
  1185.                     {
  1186.                         TempByteCount -= TempByteCount;
  1187.                     }
  1188.                 TempBuff = (BufferRec*)AllocPtrCanFail(sizeof(BufferRec),"BufferRec");
  1189.                 if (TempBuff == NIL)
  1190.                     {
  1191.                         while (LocalHead != NIL)
  1192.                             {
  1193.                                 /* dump ones that we already allocated */
  1194.                                 CheckPtrExistence(LocalHead);
  1195.                                 TempBuff = LocalHead;
  1196.                                 LocalHead = LocalHead->Next;
  1197.                                 ReleasePtr((char*)TempBuff);
  1198.                             }
  1199.                         return False; /* oops */
  1200.                     }
  1201.                 TempBuff->Next = NIL;
  1202.                 if (LocalTail != NIL)
  1203.                     {
  1204.                         LocalTail->Next = TempBuff;
  1205.                     }
  1206.                  else
  1207.                     {
  1208.                         LocalHead = TempBuff;
  1209.                     }
  1210.                 LocalTail = TempBuff;
  1211.             }
  1212.         /* buffers allocated, now fill them */
  1213.         TempByteCount = NumBytes;
  1214.         BuffScan = LocalHead;
  1215.         while (TempByteCount > 0)
  1216.             {
  1217.                 long                            NumBytesThisTime;
  1218.  
  1219.                 if (TempByteCount > BUFFERSIZE)
  1220.                     {
  1221.                         NumBytesThisTime = BUFFERSIZE;
  1222.                     }
  1223.                  else
  1224.                     {
  1225.                         NumBytesThisTime = TempByteCount;
  1226.                     }
  1227.                 CheckPtrExistence(BuffScan);
  1228.                 BuffScan->NumBytes = NumBytesThisTime;
  1229.                 CopyData(&(Buffer[0]),&(BuffScan->Buffer[0]),NumBytesThisTime);
  1230.                 Buffer += NumBytesThisTime;
  1231.                 TempByteCount -= NumBytesThisTime;
  1232.                 BuffScan = BuffScan->Next;
  1233.             }
  1234.         ERROR(BuffScan != NIL,PRERR(ForceAbort,
  1235.             "NetWriteData:  BuffScan not NIL after buffer fill loop"));
  1236.         /* now, tack it on the end of the write buffer list */
  1237.         if (Session->SendTail != NIL)
  1238.             {
  1239.                 Session->SendTail->Next = LocalHead;
  1240.             }
  1241.          else
  1242.             {
  1243.                 Session->SendHead = LocalHead;
  1244.             }
  1245.         Session->SendTail = LocalTail;
  1246.         /* if there is no outstanding write, then make a write */
  1247.         if (Session->WriteState == eWriteIdle)
  1248.             {
  1249.                 UpdateWrite(Session);
  1250.             }
  1251.         /* successful */
  1252.         return True;
  1253.     }
  1254.  
  1255.  
  1256. #ifdef THINK_C
  1257.     #if __option(profile)
  1258.         #define Profiling (True)
  1259.     #else
  1260.         #define Profiling (False)
  1261.     #endif
  1262.  
  1263.     #pragma options(!profile)
  1264. #endif
  1265.  
  1266. static pascal void    MyPPCInformCompletionRoutine(PPCInformPBRec* PB)
  1267.     {
  1268.         PortIDType*                Port;
  1269.  
  1270.         /* some goofy pointer arithmetic to get from a member of the struct */
  1271.         /* back to the beginning of the struct. */
  1272.         Port = (PortIDType*)((char*)PB - (long)&(((PortIDType*)NIL)->ThePPCInformPBRec));
  1273.         /* now we can access the other members */
  1274.         Port->PortState = eConnectionPending; /* indicate that PPCInform is done. */
  1275.     }
  1276.  
  1277. static pascal void    MyPPCReadCompletionRoutine(PPCParamBlockRec* PB)
  1278.     {
  1279.         SessionIDType*        Session;
  1280.  
  1281.         /* some goofy pointer arithmetic to get from a member of the struct */
  1282.         /* back to the beginning of the struct. */
  1283.         Session = (SessionIDType*)((char*)PB
  1284.             - (long)&(((SessionIDType*)NIL)->ThePPCReadPBRec));
  1285.         /* now we can access the other members */
  1286.         Session->ReadState = eReadFinished;
  1287.     }
  1288.  
  1289. static pascal void    MyPPCWriteCompletionRoutine(PPCParamBlockRec* PB)
  1290.     {
  1291.         SessionIDType*        Session;
  1292.  
  1293.         /* some goofy pointer arithmetic to get from a member of the struct */
  1294.         /* back to the beginning of the struct. */
  1295.         Session = (SessionIDType*)((char*)PB
  1296.             - (long)&(((SessionIDType*)NIL)->ThePPCWritePBRec));
  1297.         /* now we can access the other members */
  1298.         Session->WriteState = eWriteFinished;
  1299.     }
  1300.  
  1301. #ifdef THINK_C
  1302.     #if Profiling
  1303.         #pragma options(profile)
  1304.     #endif
  1305. #endif
  1306.